/**
 * Copyright (c) 2012 Nokia Corporation.
 */

package com.nokia.example.imagescaler;

import java.io.IOException;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.StringItem;
import javax.microedition.midlet.MIDletStateChangeException;

import org.tantalum.util.ImageUtils;

/**
 * The main view of the application.
 */
public class ImageView extends Form implements CommandListener, ItemCommandListener {
	// Constants
	private static final float[] SCALE_RATIOS = { 0.10f, 0.20f, 0.25f, 0.40f, 0.50f, 0.60f, 0.70f,
			0.80f, 1.00f, 1.40f, 1.60f, 2.00f };
	private static final String[] IMAGE_PATH = { "/map1.png","/map2.png","/map3.png","/map4.png","/map5.png","/map6.png","/map7.png","/map8.png","/phone.png",
			"/raphael_altoviti.jpg", "/buffalo.jpg", "/indonesia.png",
			"/south_korea.png", "/cambodia.png" };
	private final Command[] _scalerOptions = new Command[ImageView.NUMBER_OF_SCALING_ALGORITHMS];;
	private Command _exitCommand = new Command("Exit", Command.EXIT, 0x01);
	
	// Scalers
	public static final int LINEAR_INTERPOLATION = 0;
	public static final int BILINEAR_INTERPOLATION = 1;
	public static final int FIXED_POINT_BILINEAR_INTERPOLATION = 2;
	public static final int PIXEL_MIXING = 3;
	public static final int TANTALUM_BASIC_ONE_POINT_PICK = 4;
	public static final int TANTALUM_ONE_POINT_PICK = 5;
	public static final int TANTALUM_FIVE_POINT_BLEND = 6;
	public static final int TANTALUM_WEIGHTED_AVERAGE_OPAQUE = 7;
	public static final int TANTALUM_WEIGHTED_AVERAGE_TRANSLUCENT = 8;
	public static final int NUMBER_OF_SCALING_ALGORITHMS = 9;

	// Members
	private final Main main;
	private ImageScaler _imageScaler = null;
	private final StringItem[] _scalerButton;
	private StringItem _informativeLabel = null;
	private Gauge _imageSelectGague = null;
	private int _imagesToScale = 0;

	/**
	 * Constructor.
	 */
	public ImageView(final Main main) {
		super("Image Scaler");
		
		this.main = main;
		
		_scalerOptions[0] = new Command("Linear interpolation", Command.ITEM, 0x02);
		_scalerOptions[1] = new Command("Bilinear interpolation", Command.ITEM, 0x02);
		_scalerOptions[2] = new Command("FP Bilinear interpolation", Command.ITEM, 0x02);
		_scalerOptions[3] = new Command("Pixel mixing", Command.ITEM, 0x02);
		_scalerOptions[4] = new Command("T Basic 1Point Pick", Command.ITEM, 0x02);
		_scalerOptions[5] = new Command("T 1Point Pick", Command.ITEM, 0x02);
		_scalerOptions[6] = new Command("T 5Point Blend", Command.ITEM, 0x02);
		_scalerOptions[7] = new Command("T Weighted Average Opaque", Command.ITEM, 0x02);
		_scalerOptions[8] = new Command("T Weighted Avarage Translucent", Command.ITEM, 0x02);

		setCommandListener(this);
		addCommand(_exitCommand);

		_scalerButton = new StringItem[_scalerOptions.length];
		for (int i = 0; i < _scalerOptions.length; i++) {
			_scalerButton[i] = new StringItem("", _scalerOptions[i].getLabel(), Item.BUTTON);
			_scalerButton[i].setDefaultCommand(_scalerOptions[i]);
			_scalerButton[i].setItemCommandListener(this);
		}
		
		_informativeLabel = new StringItem("No scaler set", "n/a");
		_imageSelectGague = new Gauge("Select image", true, IMAGE_PATH.length - 1,
				0);

		// Store the number of images that will be scaled
		for (int i = 0; i < SCALE_RATIOS.length; ++i) {
			if (SCALE_RATIOS[i] != 1.0f) {
				_imagesToScale++;
			}
		}

		setImageScaler(LINEAR_INTERPOLATION);
	}


	/* 
	 * (non-Javadoc)
	 * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable)
	 */
	public void commandAction(Command command, Displayable displayable) {
		if (command == _exitCommand) {
			try {
				main.destroyApp(true);
			}
			catch (MIDletStateChangeException e) {
				e.printStackTrace();
			}
			finally {				
				main.notifyDestroyed();
			}
		}
	}

	public void commandAction(final Command command, final Item item) {
		for (int i = 0; i < ImageView.NUMBER_OF_SCALING_ALGORITHMS; i++) {
			if (command == _scalerOptions[i]) {
				setImageScaler(i);
				break;
			}
		}		
	}

	/**
	 * Sets the image scaling method.
	 * 
	 * @param type
	 *            The type of the method to set.
	 */
	public void setImageScaler(final int type) {
		switch (type) {
		case LINEAR_INTERPOLATION:
			_imageScaler = new LinearInterpolationScaler();
			break;
		case BILINEAR_INTERPOLATION:
			_imageScaler = new BilinearInterpolationScaler();
			break;
		case FIXED_POINT_BILINEAR_INTERPOLATION:
			_imageScaler = new FixedPointBilinearInterpolationScaler();
			break;
		case PIXEL_MIXING:
			_imageScaler = new PixelMixingScaler();
			break;
		case TANTALUM_BASIC_ONE_POINT_PICK:
			_imageScaler = new TantalumScaler(ImageUtils.BASIC_ONE_POINT_PICK);
			break;
		case TANTALUM_ONE_POINT_PICK:
			_imageScaler = new TantalumScaler(ImageUtils.ONE_POINT_PICK);
			break;
		case TANTALUM_FIVE_POINT_BLEND:
			_imageScaler = new TantalumScaler(ImageUtils.FIVE_POINT_BLEND);
			break;
		case TANTALUM_WEIGHTED_AVERAGE_OPAQUE:
			_imageScaler = new TantalumScaler(ImageUtils.WEIGHTED_AVERAGE_OPAQUE);
			break;
		case TANTALUM_WEIGHTED_AVERAGE_TRANSLUCENT:
			_imageScaler = new TantalumScaler(ImageUtils.WEIGHTED_AVERAGE_TRANSLUCENT);
			break;
		default:
			_imageScaler = new LinearInterpolationScaler();
			break;
		}

		_informativeLabel.setLabel(_imageScaler.type());
		createItems();
	}

	private String getCurrentImageName() {
		return IMAGE_PATH[_imageSelectGague.getValue()];
	}

	/**
	 * Scales the images based on the values in SCALE_RATIOS array in separate
	 * thread. When the method is called the first time it creates and appends
	 * the ImageItems to the Form. The times after this the content of the items
	 * is updated.
	 */
	private void createItems() {
		Thread thread = new Thread() {
			public void run() {
				try {
					deleteAll();
					for (int i = 0; i < _scalerButton.length; i++) {
						append(_scalerButton[i]);
					}
					append(_informativeLabel);
					append(_imageSelectGague);

					final Image originalImage = createImage(getCurrentImageName());
					long deltaT = 0;

					for (int i = 0; i < SCALE_RATIOS.length; ++i) {
						deltaT += showScaled(SCALE_RATIOS[i], originalImage);
					}

					_informativeLabel
							.setText(_imagesToScale + " image(s) scaled in "
									+ deltaT + "ms.");
				} catch (Exception e) {
					_informativeLabel.setText("Error: " + e);
				}
			}
		};

		thread.start();
	}
	
	private long showScaled(float scaleRatio, Image originalImage) {
		final Image scaledImage;
		
		final long t1 = System.currentTimeMillis();
		if (scaleRatio == 1.0f) {
			scaledImage = originalImage;
		} else {
			_informativeLabel.setText("Scaling image "
					+ scaleRatioPercent(scaleRatio) + "...");
			scaledImage = _imageScaler.scale(scaleRatio,
					originalImage);
		}
		final long deltaT = System.currentTimeMillis() - t1;

		append(new ImageItem(scaleRatioPercent(scaleRatio),
				scaledImage, Item.LAYOUT_CENTER, null));
		_informativeLabel.setText("Scale " + scaleRatioPercent(scaleRatio) + " done");
		
		return deltaT;
	}
	
	private String scaleRatioPercent(float scaleRatio) {
		return new String((int) (scaleRatio * 100) + " %");
	}

	/**
	 * Loads a new image from the given path.
	 * 
	 * @param path
	 *            The relative file path of the image file.
	 * @return A newly created image or null in case of an error.
	 */
	private Image createImage(String path) {
		Image image = null;

		try {
			image = Image.createImage(path);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return image;
	}
}
